package br.com.dev.tcc.view.mapa;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.location.Location;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
import br.com.dev.tcc.R;
import br.com.dev.tcc.adapter.ListaLocaisAdapter;
import br.com.dev.tcc.bean.MapLocal;
import br.com.dev.tcc.bean.Mecanica;
import br.com.dev.tcc.bean.ResponseData;
import br.com.dev.tcc.connection.WSClient;
import br.com.dev.tcc.singleton.LocationSingleton;

import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.SubMenu;
import com.actionbarsherlock.view.Window;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener;
import com.google.android.gms.maps.LocationSource;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;

public class MapaMecanicasCadastradas extends SherlockFragmentActivity {

	private SupportMapFragment mapFragment;
	private GoogleMap map;
	private ArrayList<MapLocal> locales;
	private MapNearLocalesAsync searchLocalesAsyncTask;

	private View listContainer;
	private View mapContainer;
	private Animation showAnim;
	private Animation hideAnim;
	private boolean isShowingList;
	private MenuItem menuItemLista;
	private MenuItem menuItemMapa;
	private ListView listaLocais;
	private boolean searchComplete;

	@Override
	protected void onCreate(Bundle params) {

		super.onCreate(null);

		requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
		setSupportProgressBarIndeterminateVisibility(true);

		setContentView(R.layout.mapa);

		showAnim = getShowAnim();
		hideAnim = getHideAnim();

		listContainer = findViewById(R.id.containerList);

		listaLocais = (ListView) findViewById(R.id.list);

		getSupportActionBar().setTitle(R.string.locais_mech);
		getSupportActionBar().setIcon(R.drawable.locais);
		getSupportActionBar().setHomeButtonEnabled(true);
		getSupportActionBar().setDisplayHomeAsUpEnabled(true);
		getSupportActionBar().setDisplayShowHomeEnabled(true);

		mapFragment = new SupportMapFragment() {
			@Override
			public void onActivityCreated(Bundle savedInstanceState) {

				map = getMap();
				super.onActivityCreated(savedInstanceState);
			}
		};

		mapContainer = findViewById(R.id.mapContainer);
		getSupportFragmentManager().beginTransaction()
				.replace(R.id.mapContainer, mapFragment).commit();
	}

	@Override
	protected void onStart() {

		super.onStart();
		map = mapFragment.getMap();

		if (map == null) {

			Toast.makeText(this, R.string.mapa_erro_conexao, Toast.LENGTH_LONG)
					.show();
		} else {

			map.setMapType(GoogleMap.MAP_TYPE_NORMAL);

			Location loc = LocationSingleton.getInstance().getLocation();
			LatLng latLng = null;

			if (loc != null)
				latLng = new LatLng(loc.getLatitude(), loc.getLongitude());

			map.setLocationSource(new LocationSource() {

				@Override
				public void deactivate() {
				}

				@Override
				public void activate(OnLocationChangedListener listener) {

					listener.onLocationChanged(LocationSingleton.getInstance()
							.getLocation());
				}
			});
			map.setMyLocationEnabled(true);

			CameraUpdate update = CameraUpdateFactory.newLatLngZoom(latLng, 15);
			map.animateCamera(update, 3000, null);

			map.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {

				@Override
				public void onInfoWindowClick(Marker marker) {

					for (MapLocal local : locales) {

						if (local.getName().equals(marker.getTitle())) {
							requestRoute(local.getLatLng());
						}
					}
				}
			});

			searchLocalesAsyncTask = new MapNearLocalesAsync();
			searchLocalesAsyncTask.execute();
		}
	}

	class MapNearLocalesAsync extends
			AsyncTask<Void, Void, ResponseData<List<Mecanica>>> {

		@Override
		protected ResponseData<List<Mecanica>> doInBackground(Void... loc) {

			boolean success = false;
			String message = null;
			List<Mecanica> list = null;

			try {
				if (!this.isCancelled()) {

					WSClient wsClient = new WSClient();
					list = wsClient
							.buscarEnderecosMecanica(MapaMecanicasCadastradas.this);
					success = true;
				}
			} catch (Exception e) {
				message = e.getMessage();
			}

			return new ResponseData<List<Mecanica>>(success, message, list);
		}

		@Override
		protected void onPostExecute(ResponseData<List<Mecanica>> result) {

			locales = new ArrayList<MapLocal>();

			if (!this.isCancelled()) {
				if (result.isSuccess()) {
					List<Mecanica> list = result.getData();
					for (Mecanica mecanica : list) {
						String nome = mecanica.getNome();
						String endereco = mecanica.getEndereco();
						String latitude = mecanica.getLatitude();
						String longitude = mecanica.getLongitude();
						LatLng latLng = new LatLng(
								Double.parseDouble(latitude),
								Double.parseDouble(longitude));

						addMarkerOnMap(latLng, nome, endereco);

						locales.add(new MapLocal().setAddress(endereco)
								.setLatLng(latLng).setName(nome)
								.setReference(null));
					}
				} else {
					Toast.makeText(MapaMecanicasCadastradas.this,
							result.getErrorMessage(), Toast.LENGTH_LONG).show();
				}

				searchComplete = true;
				setListLocales();

				setSupportProgressBarIndeterminateVisibility(false);
			}
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {

		SubMenu subMenu1 = menu.addSubMenu(null);
		menuItemMapa = subMenu1.getItem();
		menuItemMapa.setIcon(android.R.drawable.ic_menu_mapmode);
		menuItemMapa.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
		menuItemMapa.setChecked(true);

		SubMenu subMenu2 = menu.addSubMenu(null);
		menuItemLista = subMenu2.getItem();
		menuItemLista.setIcon(android.R.drawable.ic_menu_view);
		menuItemLista.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
		menuItemLista.setChecked(true);

		menuItemMapa.setVisible(false);
		menuItemLista.setVisible(true);

		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {

		if (item.isChecked()) {

			if (isShowingList) {

				hideList();
			} else {

				if (searchComplete) {
					showList();
				}
			}
		} else {

			cancelAsyncs();
			finish();
		}

		return true;
	}

	@Override
	public void onBackPressed() {

		if (isShowingList) {

			hideList();
		} else {

			cancelAsyncs();

			super.onBackPressed();
		}
	}

	private void cancelAsyncs() {

		if (searchLocalesAsyncTask != null)
			searchLocalesAsyncTask.cancel(true);
	}

	private void setListLocales() {

		listaLocais.setAdapter(new ListaLocaisAdapter(this, locales));

		listaLocais.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> a, View view, int position,
					long id) {

				requestRoute(locales.get(position).getLatLng());
			}
		});
	}

	private Animation getShowAnim() {

		return AnimationUtils.loadAnimation(this, R.anim.list_in);
	}

	private Animation getHideAnim() {

		return AnimationUtils.loadAnimation(this, R.anim.list_out);
	}

	private void addMarkerOnMap(LatLng l, String nome, String address) {

		if (map != null) {
			MarkerOptions m = new MarkerOptions();
			m.position(l).title(nome).snippet(address);
			map.addMarker(m);
		}
	}

	private void addAllMarkers() {

		for (MapLocal local : locales) {

			MarkerOptions m = new MarkerOptions();
			m.position(local.getLatLng()).title(local.getName())
					.snippet(local.getAddress());
			map.addMarker(m);
		}
	}

	private void showList() {

		menuItemMapa.setVisible(true);
		menuItemLista.setVisible(false);

		listContainer.setVisibility(View.VISIBLE);
		listContainer.startAnimation(showAnim);
		isShowingList = true;

		listContainer.bringToFront();
	}

	private void hideList() {

		menuItemMapa.setVisible(false);
		menuItemLista.setVisible(true);

		listContainer.startAnimation(hideAnim);
		listContainer.setVisibility(View.GONE);
		isShowingList = false;

		mapContainer.bringToFront();
	}

	private void requestRoute(LatLng destination) {

		Location location = LocationSingleton.getInstance().getLocation();

		LatLng origin = new LatLng(location.getLatitude(),
				location.getLongitude());

		new RequestDirections().execute(origin, destination);
	}

	private class RequestDirections extends
			AsyncTask<LatLng, Void, PolylineOptions> {

		@Override
		protected PolylineOptions doInBackground(LatLng... params) {

			LatLng origin = params[0];
			LatLng destination = params[1];

			try {
				String uri = "http://maps.googleapis.com/maps/api/directions/json?origin="
						+ origin.latitude
						+ ","
						+ origin.longitude
						+ "&destination="
						+ destination.latitude
						+ ","
						+ destination.longitude + "&sensor=false&mode=driving";

				HttpClient httpclient = new DefaultHttpClient();
				HttpResponse response = httpclient.execute(new HttpGet(uri));
				StatusLine statusLine = response.getStatusLine();

				if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
					ByteArrayOutputStream out = new ByteArrayOutputStream();
					response.getEntity().writeTo(out);
					out.close();

					String responseString = out.toString();

					List<LatLng> points = getRoute(responseString);

					return getPolylineOptions(points);
				} else {

					response.getEntity().getContent().close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}

			return null;
		}

		private List<LatLng> getRoute(String route) {

			List<LatLng> points = new ArrayList<LatLng>();

			try {
				JSONObject jsonObj = new JSONObject(route);
				JSONArray array = jsonObj.getJSONArray("routes");
				jsonObj = array.getJSONObject(0);
				array = jsonObj.getJSONArray("legs");
				jsonObj = array.getJSONObject(0);
				array = jsonObj.getJSONArray("steps");

				if (array.length() > 0) {

					for (int i = 0; i < array.length(); i++) {

						JSONObject location = array.getJSONObject(i)
								.getJSONObject("start_location");
						double latitude = location.getDouble("lat");
						double longitude = location.getDouble("lng");

						LatLng l = new LatLng(latitude, longitude);

						points.add(l);

						location = array.getJSONObject(i).getJSONObject(
								"end_location");
						latitude = location.getDouble("lat");
						longitude = location.getDouble("lng");

						l = new LatLng(latitude, longitude);

						points.add(l);
					}
				}
			} catch (JSONException e) {
				e.printStackTrace();
			}

			return points;
		}

		private PolylineOptions getPolylineOptions(List<LatLng> points) {

			PolylineOptions lines = new PolylineOptions();
			lines.addAll(points);

			return lines;
		}

		@Override
		protected void onPostExecute(PolylineOptions lines) {

			hideList();
			lines = lines.width(lines.getWidth() / 2);
			lines = lines.color(getResources().getColor(R.color.abs__holo_blue_light));
			lines = lines.geodesic(true);
			map.clear();
			Location loc = LocationSingleton.getInstance().getLocation();
			CameraUpdate update = CameraUpdateFactory.newLatLngZoom(new LatLng(
					loc.getLatitude(), loc.getLongitude()), 15);
			map.animateCamera(update, 1500, null);
			addAllMarkers();
			map.addPolyline(lines);
		}
	}
}
